package cn.lg.soar.system.biz.modules.general.controller;

import cn.lg.soar.common.enums.LogTypeEnum;
import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.model.PageQuery;
import cn.lg.soar.common.model.PageResult;
import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.database.query.QueryParams;
import cn.lg.soar.mvc.annotations.DisabledTenantAccess;
import cn.lg.soar.mvc.annotations.Log;
import cn.lg.soar.mvc.enums.ControllerConst;
import cn.lg.soar.mvc.util.ResponseUtils;
import cn.lg.soar.system.api.constant.ModuleConst;
import cn.lg.soar.system.api.enums.AccessModeEnum;
import cn.lg.soar.system.api.enums.ThumbnailSizeEnum;
import cn.lg.soar.system.api.model.FastUploadVO;
import cn.lg.soar.system.api.model.FileVO;
import cn.lg.soar.system.biz.config.SystemProps;
import cn.lg.soar.system.biz.filetemplate.FileTemplate;
import cn.lg.soar.system.biz.filetemplate.FileTemplateManager;
import cn.lg.soar.system.biz.filetemplate.LocalFileTemplateImpl;
import cn.lg.soar.system.biz.filetemplate.model.UploadCompleteVO;
import cn.lg.soar.system.biz.filetemplate.model.UploadInfoVO;
import cn.lg.soar.system.biz.filetemplate.model.UploadInitVO;
import cn.lg.soar.system.biz.modules.general.entity.FileMetadata;
import cn.lg.soar.system.biz.modules.general.model.FileMetadataVO;
import cn.lg.soar.system.biz.modules.general.model.UploadConfigVO;
import cn.lg.soar.system.biz.modules.general.service.FileService;
import cn.lg.soar.system.biz.modules.general.service.IFileMetadataService;
import com.amazonaws.services.s3.model.S3Object;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;
import java.util.List;

/**
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Tag(name = "文件上传控制")
@RestController
@RequestMapping(ModuleConst.MODULE_NAME + "/file")
public class FileController {

    @Autowired
    private SystemProps props;
    @Autowired
    private FileService fileService;
    @Autowired
    private IFileMetadataService fileMetadataService;
    @Autowired
    private FileTemplateManager fileTemplateManager;

    @Operation(summary = "获取配置")
    @GetMapping(path = "config", name = "获取配置")
    public HttpResult<UploadConfigVO> config() {
        UploadConfigVO configVo = new UploadConfigVO();
        configVo.setBlacklist(props.getFileUploadBlacklist());
        configVo.setWhitelist(props.getFileUploadWhitelist());
        return HttpResult.data(configVo);
    }

    @Operation(summary = "下载")
    @GetMapping("/download/{filename}")
    public void download(
            @Parameter(description = "文件名称(任意字符，前端根据业务确定)") @PathVariable("filename") String filename,
            @Parameter(description = "文件id") @RequestParam("fileId") Long fileId
    ) {
        FileMetadata fileMetadata = fileMetadataService.getById(fileId);
        AssertUtil.notNull(fileMetadata, "文件不存在");
        Integer storageId = fileMetadata.getStorageId();
        FileTemplate template = fileTemplateManager.getTemplate(storageId);
        AssertUtil.notNull(template, "文件不存在");
        S3Object object = template.getObject(filename);
        AssertUtil.notNull(object, "文件不存在");
        ResponseUtils.responseFile(object.getObjectMetadata().getContentLength(), object.getObjectContent());
    }

    @Operation(summary = "代理访问")
    @GetMapping("proxy/{storageId}/**")
    public void proxy(
            @Parameter(description = "仓库id") @PathVariable("storageId") int storageId,
            @Parameter @RequestParam(value = "s", required = false) ThumbnailSizeEnum size,
            HttpServletRequest request
    ) {
        String requestUri = request.getRequestURI();
        String p = "/" + storageId;
        int index = requestUri.indexOf(p);
        String filename = requestUri.substring(index + p.length() + 1);
        FileTemplate template = fileTemplateManager.getTemplate(storageId);
        AssertUtil.notNull(template, "文件不存在");
        S3Object object = template.getObject(filename);
        AssertUtil.notNull(object, "文件不存在");
        ResponseUtils.responseFile(object.getObjectMetadata().getContentLength(), object.getObjectContent());
    }

    @Operation(summary = "重定向访问")
    @GetMapping("redirect/{storageId}/**")
    public ResponseEntity<?> redirect(
            @Parameter(description = "仓库id") @PathVariable("storageId") int storageId,
            HttpServletRequest request
    ) throws URISyntaxException {
        String requestUri = request.getRequestURI();
        String p = "/" + storageId;
        int index = requestUri.indexOf(p);
        String filename = requestUri.substring(index + p.length() + 1);
        FileTemplate template = fileTemplateManager.getTemplate(storageId);
        AssertUtil.notNull(template, "文件不存在");
        if (template instanceof LocalFileTemplateImpl localFileTemplate) {
            // 本地文件
            localFileTemplate.download(filename);
            return null;
        }
        // 非本地文件，获取临时访问路径并重定向
        URL url = template.getAccessUrl(filename, new Date(System.currentTimeMillis() + 2 * 3600 * 1000));
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(url.toURI());
        return new ResponseEntity<>(headers, HttpStatus.FOUND);
    }

    @Operation(summary = "获取访问地址")
    @GetMapping("access/url")
    public HttpResult<String[]> presignedUrl(@RequestParam("urls") String[] urls) {
        String[] res = new String[urls.length];
        for (int i = 0; i < urls.length; i++) {
            String url = urls[i];
            int index = url.indexOf("/");
            int storageId = Integer.parseInt(url.substring(0, index));
            FileTemplate template = fileTemplateManager.getTemplate(storageId);
            URL accessUrl = template.getAccessUrl(url.substring(index + 1), new Date(System.currentTimeMillis() + 12 * 3600 * 1000));
            res[i] = accessUrl.toString();
        }
        return HttpResult.data(res);
    }

    @Operation(summary = "秒传")
    @GetMapping(path = "upload/fast", name = "秒传")
    public HttpResult<FileVO> fastUpload(FastUploadVO vo) {
        FileMetadata metadata = fileService.fastUpload(vo);
        if (metadata == null) {
            return HttpResult.data(null);
        }
        return HttpResult.data(fileService.toVo(metadata, vo.getAccessMode()));
    }

    @Operation(summary = "代理上传")
    @PostMapping(path = "upload/proxy", name = "代理上传")
    public HttpResult<FileVO> upload(
            @Parameter @RequestParam("file") MultipartFile file,
            @Parameter @RequestParam(value = "directory", required = false) String directory,
            @RequestParam(value = "accessMode", required = false) AccessModeEnum accessMode
    ) {
        if (DataUtil.isValuable(directory)) {
            FileMetadata metadata = fileService.upload(file, directory);
            return HttpResult.data(fileService.toVo(metadata, accessMode));
        }
        FileMetadata metadata = fileService.upload(file);
        return HttpResult.data(fileService.toVo(metadata, accessMode));
    }

    @Operation(summary = "获取上传地址")
    @GetMapping(path = "upload/url", name = "获取上传地址")
    public HttpResult<String> getUploadUrl(@RequestParam("filename") String filename) {
        return HttpResult.data(fileService.getUploadUrl(filename));
    }

    @Operation(summary = "初始化上传")
    @GetMapping(path = "upload/init", name = "初始化上传")
    public HttpResult<UploadInfoVO> uploadInit(@Parameter @Validated UploadInitVO vo) {
        return HttpResult.data(fileService.uploadInit(vo));
    }

    @Operation(summary = "获取分片上传地址")
    @GetMapping(path = "upload/part-url", name = "获取分片上传地址")
    public HttpResult<String> getUploadUrl(@RequestParam("uploadId") String uploadId, @RequestParam("partNumber")Integer partNumber) {
        return HttpResult.data(fileService.getUploadPartUrl(uploadId, partNumber));
    }

    @Operation(summary = "完成上传")
    @PostMapping(path = "upload/complete", name = "完成上传")
    public HttpResult<FileVO> uploadComplete(@RequestBody @Validated UploadCompleteVO vo) {
        FileMetadata metadata = fileService.uploadComplete(vo);
        return HttpResult.data(fileService.toVo(metadata, vo.getAccessMode()));
    }

    @Operation(summary = "中止上传")
    @PostMapping(path = "upload/abort", name = "中止上传")
    public HttpResult<Void> uploadAbort(@RequestParam("uploadId") String uploadId) {
        fileService.uploadAbort(uploadId);
        return HttpResult.success();
    }

    @DisabledTenantAccess
    @Operation(summary = ControllerConst.QUERY_NAME, description = "多功能查询（分页、列表、统计）")
    @RequestMapping(path = "query", method = RequestMethod.GET)
    public HttpResult<PageResult<FileMetadataVO>> query(PageQuery pageQuery, QueryParams params){
        PageResult<FileMetadataVO> result = fileService.query(pageQuery, params);
        return HttpResult.data(result);
    }

    @DisabledTenantAccess
    @Log(value = ControllerConst.REMOVE_NAME, type = LogTypeEnum.REMOVE)
    @Operation(summary = ControllerConst.REMOVE_NAME, description = "根据 id 删除")
    @RequestMapping(path = "remove", method = RequestMethod.POST)
    public HttpResult<Void> remove(@RequestBody List<Long> ids) {
        fileService.remove(ids);
        return HttpResult.success();
    }

}
